package shardkv

import "sync"
import "shardmaster"

type Clerk2 struct {
  mu sync.Mutex // one RPC at a time
  sm *shardmaster.Clerk
  config shardmaster.Config
  // You'll have to modify Clerk.
}


func MakeClerk2(shardmasters []string) *Clerk2 {
  ck := new(Clerk2)
  ck.sm = shardmaster.MakeClerk(shardmasters)
  // You'll have to modify MakeClerk.
  return ck
}

func (ck *Clerk2) Prepare(gid int64, tran_id int64, pairs []Pair) bool {
  ck.mu.Lock()
  defer ck.mu.Unlock()

  UID := nrand()
  ck.config = ck.sm.Query(-1)
  servers, ok := ck.config.Groups[gid]

  if ok {
    // try each server in the shard's replication group.
    for _, srv := range servers {
      args := &PrepareArgs{}
      args.UID = UID
			args.TranID = tran_id
    	args.Pairs = pairs
      var reply PrepareReply
      ok := call(srv, "ShardKV.Prepare", args, &reply)
      if ok && reply.Err == OK {
        return true
      }
    }
  }
	return false
}

func (ck *Clerk2) Commit(gid int64, tran_id int64) Err {
	ck.mu.Lock()
	defer ck.mu.Unlock()

	UID := nrand()
	ck.config = ck.sm.Query(-1)
	servers, ok := ck.config.Groups[gid]

	err := Err("")
	if ok {
		// try each server in the shard's replication group.
		for _, srv := range servers {
			args := &PrepareArgs{}
			args.UID = UID
			args.TranID = tran_id
			var reply PrepareReply
			ok := call(srv, "ShardKV.Commit", args, &reply)
			if ok && reply.Err == OK {
				return OK
			}
			if ok {
				err += reply.Err + " "
			}
		}
	}
	return err
}

func (ck *Clerk2) Abort(gid int64, tran_id int64) Err {
	ck.mu.Lock()
	defer ck.mu.Unlock()

	UID := nrand()
	ck.config = ck.sm.Query(-1)
	servers, ok := ck.config.Groups[gid]

	err := Err("")
	if ok {
		// try each server in the shard's replication group.
		for _, srv := range servers {
			args := &PrepareArgs{}
			args.UID = UID
			args.TranID = tran_id
			var reply PrepareReply
			ok := call(srv, "ShardKV.Abort", args, &reply)
			if ok && reply.Err == OK {
				return OK
			}
			if ok {
				err += reply.Err + " "
			}
		}
	}
	return err
}

